/**
 * 
 */
package br.edu.ufcg.embedded.mysocial;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.LinkedHashSet;
import java.util.Set;

import org.jgrapht.Graph;
import org.jgrapht.event.ConnectedComponentTraversalEvent;
import org.jgrapht.event.EdgeTraversalEvent;
import org.jgrapht.event.TraversalListenerAdapter;
import org.jgrapht.event.VertexTraversalEvent;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.traverse.DepthFirstIterator;
import br.embedded.socialapp.business.Usuario;

/**
 * @author daniel
 *
 */
public class GraphCache extends TraversalListenerAdapter<Usuario, DefaultEdge>
{
	private Graph<Usuario, DefaultEdge> graph;
	private Hashtable<String, Hashtable<Integer, LinkedHashSet<Usuario>>> hashtable;
	
	public GraphCache(Graph<Usuario, DefaultEdge> g)
	{
		this.graph = g;
		this.hashtable = new Hashtable<String, Hashtable<Integer, LinkedHashSet<Usuario>>>();
	}
	
	/* (non-Javadoc)
	 * @see org.jgrapht.event.TraversalListenerAdapter#edgeTraversed(org.jgrapht.event.EdgeTraversalEvent)
	 */
	@Override
	public void edgeTraversed(EdgeTraversalEvent<Usuario, DefaultEdge> arg0) {
		super.edgeTraversed(arg0);
		
		Usuario v1 = graph.getEdgeSource(arg0.getEdge());
		Usuario v2 = graph.getEdgeTarget(arg0.getEdge());
		
		if (hashtable.containsKey(v1.getLogin()))
		{
			if (!hashtable.get(v1.getLogin()).containsKey(1))
				hashtable.get(v1.getLogin()).put(1, new LinkedHashSet<Usuario>());
			
			hashtable.get(v1.getLogin()).get(1).add(v2);
		}
		
		if (hashtable.containsKey(v2.getLogin()))
		{
			if (!hashtable.get(v2.getLogin()).containsKey(1))
				hashtable.get(v2.getLogin()).put(1, new LinkedHashSet<Usuario>());
			
			hashtable.get(v2.getLogin()).get(1).add(v1);
		}
	}

	/* (non-Javadoc)
	 * @see org.jgrapht.event.TraversalListenerAdapter#vertexFinished(org.jgrapht.event.VertexTraversalEvent)
	 */
	@Override
	public void vertexFinished(VertexTraversalEvent<Usuario> arg0) {
		super.vertexFinished(arg0);
		
		Usuario vertex = arg0.getVertex();
		
		update(vertex);
	}
	
	private void update(Usuario vertex) {
		if (hashtable.containsKey(vertex.getLogin()))
		{
			String vertexName = vertex.getLogin();
				
			Set<Usuario> vertexNeighbours = hashtable.get(vertexName).get(1); //cópia de todos os vizinhos n(1) do vértice que terminou

			if (vertexNeighbours != null && vertexNeighbours.size() > 0)
			{
				for (Usuario neighbour : vertexNeighbours) //o vértice que terminou vai copiar para todos os seus vizinhos as informações que ele conhece. Todos os vizinhos n(i)
				{
					ArrayList<Usuario> diffList = null;

					for (int i = 1; i <= hashtable.get(vertexName).size(); i++)
					{
						if (!hashtable.get(vertexName).containsKey(i) || hashtable.get(vertexName).get(i).isEmpty())
							continue;
						
						String neighbourName = neighbour.getLogin();
						
						if (!hashtable.get(neighbourName).containsKey(i+1)) //cria próximo nível na tabela do vizinho corrente, caso ainda não exista.
							hashtable.get(neighbourName).put(i+1, new LinkedHashSet<Usuario>());
						
						diffList = getDiff(i, vertexName, neighbourName);
						
						if (diffList.size() == 0 && hashtable.get(neighbourName).get(i+1).isEmpty())
						{
							hashtable.get(neighbourName).remove(i+1);
						}
						
						if (hashtable.get(neighbourName).containsKey(i+1))
						{
							hashtable.get(neighbourName).get(i+1).addAll(diffList);

							if (i > 1)
							{
								for (Usuario usuario : diffList) {
									
									String login = usuario.getLogin();
									
									if (!hashtable.get(login).containsKey(i+1))
										hashtable.get(login).put(i+1, new LinkedHashSet<Usuario>());
									
									hashtable.get(login).get(i+1).add(neighbour);
								}
							}
						}
					}
				}
			}
		}
	}
	
	private ArrayList<Usuario> getDiff(int sourceLevel, String source, String target)
	{
		ArrayList<Usuario> diffList = new ArrayList<Usuario>();
		boolean skip = false;
		
		for (Usuario neighbour : hashtable.get(source).get(sourceLevel)) //vizinhos
		{

			if (neighbour.getLogin().equalsIgnoreCase(target))
				continue;
			
			for (int j = 1; j <= hashtable.get(target).size(); j++)
			{
				if (hashtable.get(target).containsKey(j) && hashtable.get(target).get(j).contains(neighbour))
				{
					skip = true;
					break;
				}
			}
			if (!skip)
				diffList.add(neighbour);
			skip = false;
		}
		return diffList;
	}

/*	private void update(Usuario vertex) {
		if (hashtable.containsKey(vertex.getLogin()))
		{
			Set<Usuario> vertexNeighbours = hashtable.get(vertex.getLogin()).get(level);
			
			if (vertexNeighbours != null && vertexNeighbours.size() > 0)
			{
				int nextLevel = level + 1;
				
				for (Usuario neighbour : vertexNeighbours)
				{
					if (!hashtable.get(neighbour.getLogin()).containsKey(nextLevel))
						hashtable.get(neighbour.getLogin()).put(nextLevel, new HashSet<Usuario>());
					
					Set<Usuario> s1 = new HashSet<Usuario>();
					
					ArrayList<Usuario> n1 = new ArrayList<Usuario>(hashtable.get(neighbour.getLogin()).get(level));
					ArrayList<Usuario> n2 = new ArrayList<Usuario>(hashtable.get(neighbour.getLogin()).get(nextLevel));
					
					s1.addAll(n1);
					s1.addAll(n2);
					
					ArrayList<Usuario> s2 = new ArrayList<Usuario>(vertexNeighbours);
					s2.remove(neighbour);
					
					for (Usuario Usuario : s1) {
						s2.remove(Usuario);
					}
				
					hashtable.get(neighbour.getLogin()).get(nextLevel).addAll(s2);
					n2 = new ArrayList<Usuario>(hashtable.get(neighbour.getLogin()).get(nextLevel));
					
					for (Usuario Usuario : n1) {
						if (n2.contains(Usuario))
							n2.remove(Usuario);
					}
					hashtable.get(neighbour.getLogin()).put(nextLevel, new HashSet<Usuario>(n2));
					
					//System.out.println(n2);
					//System.out.println("v: " + vertex.getLogin() + " neig: " + neighbour.getLogin() +  " n1: " + hashtable.get(neighbour.getLogin()).get(level) + " n2: " + hashtable.get(neighbour.getLogin()).get(nextLevel));
					//hashtable.get(neighbour.getLogin()).get(nextLevel).remove(neighbour);
					 
				}
			}
		}
	}
*/
	/* (non-Javadoc)
	 * @see org.jgrapht.event.TraversalListenerAdapter#vertexTraversed(org.jgrapht.event.VertexTraversalEvent)
	 */
	@Override
	public void vertexTraversed(VertexTraversalEvent<Usuario> arg0) {
		
		super.vertexTraversed(arg0);
		
		Usuario vertex = arg0.getVertex();
		
		if (!hashtable.containsKey(vertex.getLogin()))
		{
			hashtable.put(vertex.getLogin(), new Hashtable<Integer, LinkedHashSet<Usuario>>());
		}
	}
	
	public Set<Usuario> getNeighboursAtLevel(String vertex, int level)
	{
		if (containsNeighboursAtLevel(vertex, level))
			return hashtable.get(vertex).get(level);
		
		return null;
	}
	
	public boolean containsNeighboursAtLevel(String vertex, int level)
	{
		return (hashtable.containsKey(vertex) && hashtable.get(vertex).containsKey(level)); 
	}
	
	public Set<String> getAllVertex()
	{
		return hashtable.keySet();
	}
	
	public void cacheFriendshipLevels()
	{
		DepthFirstIterator<Usuario, DefaultEdge> iterator = new DepthFirstIterator<Usuario, DefaultEdge>(graph);
		
		iterator.setCrossComponentTraversal(true);
		iterator.addTraversalListener(this);
		
		while (iterator.hasNext())
			iterator.next();
	}
	
	@Override
	public void connectedComponentFinished(ConnectedComponentTraversalEvent e) {
		// TODO Auto-generated method stub
		super.connectedComponentFinished(e);
		
		//System.out.println(hashtable);
	}
}
